package kino.server;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 服务端运行状态
 */
public class KinoServerRuntime {

    /**
     * 运行时内存消耗（字节）
     */
    @KinoServerRuntimeGroup(groupName = "Memory")
    private Integer usedMemory;

    /**
     * 运行时内存消耗（MB）
     */
    @KinoServerRuntimeGroup(groupName = "Memory")
    private String usedMemoryHuman;

    /**
     * 运行时峰值内存使用量（字节）
     */
    @KinoServerRuntimeGroup(groupName = "Memory")
    private Integer usedMemoryPeak;

    /**
     * 运行时峰值内存使用量（MB）
     */
    @KinoServerRuntimeGroup(groupName = "Memory")
    private String usedMemoryPeakHuman;

    /**
     * 单例
     */
    private static KinoServerRuntime instance = new KinoServerRuntime();

    /**
     * 获取单例
     * @return
     */
    public static KinoServerRuntime getInstance() {
        return instance;
    }

    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        Class runtimeClass = KinoServerRuntime.class;
        Field[] fields = runtimeClass.getDeclaredFields();
        Map<String, Map<String, String>> printMap = new HashMap<>();
        // 分组打印
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                Object valueObj = field.get(this);
                if (valueObj == null) {
                    continue;
                }
                KinoServerRuntimeGroup kinoServerRuntimeGroup = field.getAnnotation(KinoServerRuntimeGroup.class);
                if (kinoServerRuntimeGroup == null) {
                    continue;
                }
                Map<String, String> itemGroup;
                if (printMap.containsKey(kinoServerRuntimeGroup.groupName())) {
                    itemGroup = printMap.get(kinoServerRuntimeGroup.groupName());
                } else {
                    itemGroup = new HashMap<>();
                    printMap.put(kinoServerRuntimeGroup.groupName(), itemGroup);
                }
                itemGroup.put(this.camelToUnderline(field.getName()), valueObj.toString());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        for (String key : printMap.keySet()) {
            stringBuffer.append("# " + key + "\n");
            Map<String, String> itemGroup = printMap.get(key);
            for (String itemKey : itemGroup.keySet()) {
                stringBuffer.append(itemKey + ":" + itemGroup.get(itemKey) + "\n");
            }
            stringBuffer.append("\n");
        }
        return stringBuffer.toString();
    }

    /**
     * 驼峰转下划线
     * @param line
     * @return
     */
    public static String camelToUnderline(String line){
        if(line==null || "".equals(line)){
            return "";
        }
        line=String.valueOf(line.charAt(0)).toUpperCase().concat(line.substring(1));
        StringBuffer sb=new StringBuffer();
        Pattern pattern=Pattern.compile("[A-Z]([a-z\\d]+)?");
        Matcher matcher=pattern.matcher(line);
        while(matcher.find()){
            String word=matcher.group();
            sb.append(word.toLowerCase(Locale.ROOT));
            sb.append(matcher.end() == line.length() ? "" : "_");
        }
        return sb.toString();
    }

    public Integer getUsedMemory() {
        return usedMemory;
    }

    public void setUsedMemory(Integer usedMemory) {
        this.usedMemory = usedMemory;
    }

    public String getUsedMemoryHuman() {
        return usedMemoryHuman;
    }

    public void setUsedMemoryHuman(String usedMemoryHuman) {
        this.usedMemoryHuman = usedMemoryHuman;
    }

    public Integer getUsedMemoryPeak() {
        return usedMemoryPeak;
    }

    public void setUsedMemoryPeak(Integer usedMemoryPeak) {
        this.usedMemoryPeak = usedMemoryPeak;
    }

    public String getUsedMemoryPeakHuman() {
        return usedMemoryPeakHuman;
    }

    public void setUsedMemoryPeakHuman(String usedMemoryPeakHuman) {
        this.usedMemoryPeakHuman = usedMemoryPeakHuman;
    }
}
